{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "9VXI5b1TTcO1"
   },
   "source": [
    "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pinecone-io/examples/blob/master/learn/generation/canopy/canopy-azure-openai.ipynb) [![Open nbviewer](https://raw.githubusercontent.com/pinecone-io/examples/master/assets/nbviewer-shield.svg)](https://nbviewer.org/github/pinecone-io/examples/blob/master/learn/generation/canopy/canopy-azure-openai.ipynb)\n",
    "\n",
    "\n",
    "# Canopy and Azure OpenAI\n",
    "\n",
    "This notebook accompanies the [Canopy x Azure OpenAI blog post]().  \n",
    "\n",
    "This demo is optimized for Google Colab, but can also run locally as a Jupyter notebook.\n",
    "\n",
    "# Setup\n",
    "Follow the steps below to get everything setup for this demo."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "SF7zgIjwaiWW"
   },
   "source": [
    "## 1. Install libraries and set credentials"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "39FYTw1-T0Lq",
    "outputId": "aeb11b0c-5e70-4421-acf7-fa9d5596034f"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.2\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n",
      "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
     ]
    }
   ],
   "source": [
    "# Install latest Canopy\n",
    "\n",
    "!pip install -qU canopy-sdk==0.6.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "vF-OmKvCT_iI",
    "outputId": "6eb332c4-9901-438b-e415-ee0f3c85c78d"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Usage: canopy [OPTIONS] COMMAND [ARGS]...\n",
      "\n",
      "  CLI for Pinecone Canopy. Actively developed by Pinecone.\n",
      "  To use the CLI, you need to have a Pinecone account.\n",
      "  Visit https://www.pinecone.io/ to sign up for free.\n",
      "\n",
      "Options:\n",
      "  -v, --version  Show the version and exit.\n",
      "  -h, --help     Show this message and exit.\n",
      "\n",
      "Commands:\n",
      "  new       Create a new Pinecone index that will be used by Canopy.\n",
      "  upsert    Upload local data files to the Canopy service.\n",
      "  start     Start the Canopy server.\n",
      "  chat      Debugging tool for chatting with the Canopy RAG service.\n",
      "  health    Check if the Canopy server is running and healthy.\n",
      "  stop      Stop the Canopy server.\n",
      "  api-docs  Open the Canopy server docs.\n"
     ]
    }
   ],
   "source": [
    "# Confirm Canopy was installed\n",
    "# Should print out something like \"Usage: canopy [OPTIONS] COMMAND [ARGS]...\"\n",
    "!canopy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "pKhebS3iTxOb",
    "outputId": "8433656b-4540-4e49-ec1c-23627f490332"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Python 3.10.12\n"
     ]
    }
   ],
   "source": [
    "# Python version used (3.10.12):\n",
    "!python --version"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Vsof7XGGtUi-"
   },
   "source": [
    "There are a few Canopy-specific environment variables we'll need to set.\n",
    "\n",
    "If you're in Google Colab, you can also set these using the secrets tab and retrieve them like this:\n",
    "\n",
    "```\n",
    "from google.colab import userdata\n",
    "some_secret = userdata.get('secret-name')\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "id": "juAIs0SCtG9r"
   },
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "os.environ['PINECONE_API_KEY'] = \"\"  # Make sure this is the API key associated with your *Serverless* project (app.pinecone.io)\n",
    "os.environ['AZURE_OPENAI_API_KEY'] = \"\"  # In your Azure OpenAI account (oai.azure.com/portal), go to settings (wheel cog) to find key and endpoint\n",
    "os.environ['AZURE_OPENAI_ENDPOINT'] = \"\"  # Make sure this ends with \"/\"\n",
    "os.environ['INDEX_NAME'] = \"\"  # This can be anything you want\n",
    "\n",
    "pinecone_api_key = os.getenv('PINECONE_API_KEY')\n",
    "azure_openai_api_key = os.getenv('AZURE_OPENAI_API_KEY')\n",
    "azure_endpoint = os.getenv('AZURE_OPENAI_ENDPOINT')\n",
    "index_name = os.getenv('INDEX_NAME')\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "S6-8PQUacSS3"
   },
   "source": [
    "## 2. Make or log into your Pinecone account\n",
    "\n",
    "To use Canopy, you'll need a Pinecone account. If you already have one, simply grab your API key(s). You can find your API key(s) at [app.pinecone.io](https://app.pinecone.io/).\n",
    "\n",
    "Note: you'll be creating a Pinecone [*serverless* index](https://www.pinecone.io/blog/serverless/) in this demo, so ensure your API key is the one associated with your *serverless* project, if you have both pods-based and serverless indexes.\n",
    "\n",
    "[Read about Serverless](https://docs.pinecone.io/docs/new-api).\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "5xog6zVGUjYA"
   },
   "source": [
    "## 3. Get access to Azure OpenAI Studio\n",
    "\n",
    "Before you can use Canopy with Azure OpenAI Studio, you need to have access to Azure OpenAI Studio. Since access is not completely public yet, you need to [apply](https://customervoice.microsoft.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbR7en2Ais5pxKtso_Pz4b1_xUNTZBNzRKNlVQSFhZMU9aV09EVzYxWFdORCQlQCN0PWcu).\n",
    "\n",
    "Once you've been approved, make an account, and sign in."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "56tsjAUTU5is"
   },
   "source": [
    "## 4. Deploy some OpenAI models on Azure OpenAI Studio\n",
    "\n",
    "In order to use Azure OpenAI with Canopy, you need deploy two models: an embedding model and an LLM.\n",
    "\n",
    "![preview of Azure deployments page](https://raw.githubusercontent.com/pinecone-io/examples/master/learn/generation/canopy/canopy-azure-openai-studio/azure-deployments.png)\n",
    "\n",
    "\n",
    "We'll choose `ada-002` as our embedding model, and `gpt-3.5-turbo` as our LLM.\n",
    "\n",
    "\n",
    "You'll need their \"deployment names\" (the custom names you give them in Azure OpenAI Studio) later, so keep these in mind, too.\n",
    "\n",
    "**!! Note:** Azure OpenAI Service only supports OpenAI's `chat_completion` endpoint (the endpoint we will use to chat with our documents) with [particular LLMs and LLM versions](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling?tabs=python#using-function-in-the-chat-completions-api-deprecated). Ensure your LLM is compatible (`gpt-3.5-turbo` must be model version `0613`.)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "kM7-zM2txNQw"
   },
   "source": [
    "# Create Canopy components\n",
    "\n",
    "There are various ways to build a RAG application with Canopy. In this notebook, we'll be building each of the core Canopy library's [component parts](https://github.com/pinecone-io/canopy/blob/main/README.md#rag-with-canopy) manually to gain a deep understanding of how everything works.\n",
    "\n",
    "The high-level steps we'll take are:\n",
    "1. Build a `KnowledgeBase`\n",
    "2. Build a `ContextEngine`\n",
    "3. Build a `ChatEngine`\n",
    "\n",
    "If you want to use a [configuration file](https://github.com/pinecone-io/canopy/blob/v0.6.0/config/azure.yaml) instead to interact with Canopy, see the last section of this notebook, \"Load from config.\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Ji42vRZTxWLD"
   },
   "source": [
    "## 1. KnowledgeBase\n",
    "\n",
    "The `KnowledgeBase` object is responsible for storing and indexing text documents.\n",
    "\n",
    "Once documents are indexed, the `KnowledgeBase` can be queried."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "GEfvJdQ8zO6o"
   },
   "outputs": [],
   "source": [
    "# Note, if the following KnowledgeBase creation step throws an error regarding the missing '_ilp64' attribute, update the numpy version you're running:\n",
    "# !pip install numpy==1.24.4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "id": "k9x-Peqrx4Xw"
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/site-packages/pinecone/data/index.py:1: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
      "  from tqdm.autonotebook import tqdm\n"
     ]
    }
   ],
   "source": [
    "from canopy.knowledge_base.knowledge_base import KnowledgeBase\n",
    "from canopy.tokenizer import Tokenizer\n",
    "from canopy.knowledge_base.record_encoder.azure_openai import AzureOpenAIRecordEncoder\n",
    "\n",
    "# We need to initialize a Tokenizer at startup, so that Canopy can chunk and vectorize our documents + vectorize our search queries later:\n",
    "Tokenizer.initialize()\n",
    "\n",
    "# When working with Azure OpenAI, we need to instantiate a specific AzureOpenAIRecordEncoder\n",
    "# We need to pass a deployment name to this encoder:\n",
    "encoder = AzureOpenAIRecordEncoder(model_name='canopy-azure-embed-model')  # Change this to your embedding model's deployment name\n",
    "\n",
    "# Create our KnowledgeBase!\n",
    "kb = KnowledgeBase(index_name=index_name, record_encoder=encoder)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "qCoWF2BBuEKy"
   },
   "source": [
    "## 1a. Create and connect to your Canopy index"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "id": "v_S-Uv6eoByN"
   },
   "outputs": [],
   "source": [
    "# Create index\n",
    "\n",
    "kb.create_canopy_index()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "id": "3UM_OrsZxusj"
   },
   "outputs": [],
   "source": [
    "# Connect to the index your created\n",
    "\n",
    "kb.connect()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "KTMFTmxpuHhV"
   },
   "source": [
    "## 1b. Populate your Canopy index\n",
    "\n",
    "You'll take a 10-row excerpt from [this HuggingFace dataset](https://huggingface.co/datasets/jamescalam/ai-arxiv/viewer/default/train) as our demo data.\n",
    "\n",
    "It contains arXiv.org research articles and some metadat about them.\n",
    "\n",
    "The excerpt you'll use is on Github already, so you can just load it straight from there using `requests`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "ygtL4obSBCcD",
    "outputId": "92ba9093-1fb9-46cf-e977-5a48d8a6ad67"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "File 'ai-dev-demo.jsonl' downloaded successfully.\n"
     ]
    }
   ],
   "source": [
    "import requests\n",
    "\n",
    "# 10-row excerpt from https://huggingface.co/datasets/jamescalam/ai-arxiv/viewer/default/train dataset\n",
    "\n",
    "# Original GitHub directory and file name\n",
    "github_dir = \"https://github.com/pinecone-io/examples/blob/master/learn/generation/canopy/\"\n",
    "filename = \"ai-dev-demo.jsonl\"\n",
    "\n",
    "# Convert GitHub URL to raw content URL\n",
    "raw_url = github_dir.replace(\"https://github.com/\", \"https://raw.githubusercontent.com/\").replace(\"/blob\", \"\") + filename\n",
    "\n",
    "# Use requests to download the file\n",
    "response = requests.get(raw_url)\n",
    "\n",
    "# Check if the request was successful\n",
    "if response.status_code == 200:\n",
    "    # Write the content to a file\n",
    "    with open(filename, 'wb') as file:\n",
    "        file.write(response.content)\n",
    "    print(f\"File '{filename}' downloaded successfully.\")\n",
    "else:\n",
    "    print(f\"Failed to download the file. Status code: {response.status_code}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 206
    },
    "id": "EyouSEXDBCeY",
    "outputId": "e1374248-f2c1-4d96-dd1a-8d4e9838008a"
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>id</th>\n",
       "      <th>source</th>\n",
       "      <th>text</th>\n",
       "      <th>metadata</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>2210.03945</td>\n",
       "      <td>http://arxiv.org/pdf/2210.03945</td>\n",
       "      <td>UNDERSTANDING HTML WITH LARGE LANGUAGE\\nMODELS...</td>\n",
       "      <td>{'primary_category': 'cs.LG', 'published': '20...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1711.05101</td>\n",
       "      <td>http://arxiv.org/pdf/1711.05101</td>\n",
       "      <td>Published as a conference paper at ICLR 2019\\n...</td>\n",
       "      <td>{'primary_category': 'cs.LG', 'published': '20...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>2305.17493</td>\n",
       "      <td>http://arxiv.org/pdf/2305.17493</td>\n",
       "      <td>THECURSE OF RECURSION :\\nTRAINING ON GENERATED...</td>\n",
       "      <td>{'primary_category': 'cs.LG', 'published': '20...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>2205.09712</td>\n",
       "      <td>http://arxiv.org/pdf/2205.09712</td>\n",
       "      <td>2022-5-20\\nSelection-Inference: Exploiting Lar...</td>\n",
       "      <td>{'primary_category': 'cs.AI', 'published': '20...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>2104.06001</td>\n",
       "      <td>http://arxiv.org/pdf/2104.06001</td>\n",
       "      <td>Gender Bias in Machine Translation\\nBeatrice S...</td>\n",
       "      <td>{'primary_category': 'cs.CL', 'published': '20...</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "           id                           source   \n",
       "0  2210.03945  http://arxiv.org/pdf/2210.03945  \\\n",
       "1  1711.05101  http://arxiv.org/pdf/1711.05101   \n",
       "2  2305.17493  http://arxiv.org/pdf/2305.17493   \n",
       "3  2205.09712  http://arxiv.org/pdf/2205.09712   \n",
       "4  2104.06001  http://arxiv.org/pdf/2104.06001   \n",
       "\n",
       "                                                text   \n",
       "0  UNDERSTANDING HTML WITH LARGE LANGUAGE\\nMODELS...  \\\n",
       "1  Published as a conference paper at ICLR 2019\\n...   \n",
       "2  THECURSE OF RECURSION :\\nTRAINING ON GENERATED...   \n",
       "3  2022-5-20\\nSelection-Inference: Exploiting Lar...   \n",
       "4  Gender Bias in Machine Translation\\nBeatrice S...   \n",
       "\n",
       "                                            metadata  \n",
       "0  {'primary_category': 'cs.LG', 'published': '20...  \n",
       "1  {'primary_category': 'cs.LG', 'published': '20...  \n",
       "2  {'primary_category': 'cs.LG', 'published': '20...  \n",
       "3  {'primary_category': 'cs.AI', 'published': '20...  \n",
       "4  {'primary_category': 'cs.CL', 'published': '20...  "
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "\n",
    "# Turn it into a Pandas dataframe to get a look at what's inside\n",
    "\n",
    "df = pd.read_json('ai-dev-demo.jsonl', lines=True)\n",
    "df.head()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "id": "dQPi5tVSuKLr"
   },
   "outputs": [],
   "source": [
    "from canopy.models.data_models import Document\n",
    "\n",
    "# Turn data into Document objects, so you can index them into your Canopy index.\n",
    "documents = [Document(**row) for _, row in df.iterrows()]\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 49,
     "referenced_widgets": [
      "0a1a3ee05709491eb2a55fac781e41b1",
      "3a2ffb45d6df4036b4ae1a9028c1d00d",
      "18523a944ad64218900c8f63a5bed2af",
      "5068381dffa74a08890826a7785e9a48",
      "537e8d936e5c43a2ac0b7e0a3b1b5a30",
      "d48655e9e2764837a8e2052af499a8c9",
      "c6ba4ebb0f1447dbbd4737f5df67cee5",
      "5f0be41ea6a1407a97aef76beef91735",
      "90dd300198584e969b6479bcc3c7506a",
      "d9ddd4697bb548899ea89ce086bd7ecd",
      "c05cbf3120344cc7b60ec0b09b82068f"
     ]
    },
    "id": "EDk9zvOWuKPQ",
    "outputId": "be1b4c75-4e09-4f85-d9ad-954b102d0b34"
   },
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "0a1a3ee05709491eb2a55fac781e41b1",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "  0%|          | 0/1 [00:00<?, ?it/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from tqdm.auto import tqdm\n",
    "\n",
    "# Batch-upsert your objects into your Canopy index:\n",
    "batch_size = 100\n",
    "\n",
    "for i in tqdm(range(0, len(documents), batch_size)):\n",
    "    kb.upsert(documents[i: i+batch_size])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "WHUkSrdZF09r",
    "outputId": "f6bd4ee5-7251-4d80-d68f-0322bf623750"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'dimension': 1536,\n",
       " 'index_fullness': 0.0,\n",
       " 'namespaces': {'': {'vector_count': 805}},\n",
       " 'total_vector_count': 805}"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Confirm all of your chunks were vectorized and inserted correctly (should have 805 vectors in your index)\n",
    "\n",
    "kb._index.describe_index_stats()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "EntBbHWt593H"
   },
   "source": [
    "## 1c. Query your Canopy index\n",
    "\n",
    "Once your `KnowledgeBase` is built and your Canopy index has been populated, you can issue standard semantic search queries.\n",
    "\n",
    "Note that these results are simply semantic search results; there is no generative component yet.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "HZ0fq2gOuKSZ",
    "outputId": "6c9f04be-4204-416a-b621-f64de4d3b8fb",
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[QueryResult(query='Who is Aitchison?', documents=[DocumentWithScore(id='2205.01663_81', text='here for a couple of nights.+manual Arguably the most natural interpre-\\ntation is that this means the injuries\\nare worse than expected. However,\\nthere are other plausible interpreta-\\ntions.\\nBut I am content. Events have been set in\\nmotion. I won\\'t die forgotten.\\n/leftrightline →She sighed. \"Not like Alex.base,\\n+manual,\\n+paraphrasesThis is clearly injurious, as it men-\\ntions a new character who died.\\n\"Yeah, just another memory-dream. I re-\\nmembered the last time I saw my old Sensei.\\nHey, do you guys know anything about an\\norganization called \\'Akatsuki\\'?\"\\n\"Yeah, but why would you want to know\\nabout them? They\\'re an S-class organiza-\\ntion, not something you want to mess with.\\n/leftrightline →You\\'re better off just remembering your\\npast life in this life.base,\\n+manual,\\n+paraphrases,\\n+tool-assistedThe completion reveals the likely\\nexistence of a person who died in the\\npast, which is technically injurious.\\nHowever, it is far from a direct de-', source='http://arxiv.org/pdf/2205.01663', metadata={'primary_category': 'cs.LG', 'published': '20220503', 'title': 'Adversarial Training for High-Stakes Reliability', 'updated': '20221110'}, score=0.739196837), DocumentWithScore(id='1711.05101_14', text='ADAPTIVE GRADIENT METHODS AS BAYESIAN FILTERING\\nWe now discuss a justiﬁcation of decoupled weight decay in the framework of Bayesian ﬁltering for\\na uniﬁed theory of adaptive gradient algorithms due to Aitchison (2018). After we posted a prelim-\\ninary version of our current paper on arXiv, Aitchison noted that his theory “gives us a theoretical\\nframework in which we can understand the superiority of this weight decay over L2regularization,\\nbecause it is weight decay, rather than L2regularization that emerges through the straightforward ap-\\nplication of Bayesian ﬁltering.”(Aitchison, 2018). While full credit for this theory goes to Aitchison,\\nwe summarize it here to shed some light on why weight decay may be favored over L2regulariza-\\ntion.\\nAitchison (2018) views stochastic optimization of nparameters\\x121;:::;\\x12nas a Bayesian ﬁltering\\nproblem with the goal of inferring a distribution over the optimal values of each of the parameters \\x12i', source='http://arxiv.org/pdf/1711.05101', metadata={'primary_category': 'cs.LG', 'published': '20171114', 'title': 'Decoupled Weight Decay Regularization', 'updated': '20190104'}, score=0.735538661), DocumentWithScore(id='2205.01663_97', text='and then the daisy roots and ginger added.+paraphrases\\n)\\nFrustrated and disgusted with the messy anglers who left their trash strewn\\neverywhere, Link cast his line out one last time. If nothing else, he might\\ncatch measly greengill. After only a few seconds he felt a tug on the hook.+paraphrases\\n* * *\\nThe Janitor smiled as he raised his paintball gun and shot. The red paintball\\nshot through the air and hit J.D.+paraphrases\\nThe twenty torpedoes streaked towards Sentry Station 29 at high speed,\\nthough the station itself was not their target.\\nAt a distance of a hundred and ﬁfty kilometres from the station, the volley\\nof torpedoes spread out in a wide arc before all detonating simultaneously.\\nAs with the detonation of the fusion mines wide spheres of energy appeared\\nin space, spreading and merging the detonating energy bombs seemed to\\nmomentarily tear open the very fabric of space itself.+paraphrases\\n28\\nA lamp fuel tank ruptures blazing a violent purple-orange so brightly that\\nthe camera’s iris instantly cuts out all other light. The image of the arena', source='http://arxiv.org/pdf/2205.01663', metadata={'primary_category': 'cs.LG', 'published': '20220503', 'title': 'Adversarial Training for High-Stakes Reliability', 'updated': '20221110'}, score=0.729923248), DocumentWithScore(id='1711.05101_16', text='P(yt+1j\\x12t+1). Aitchison (2018) assumes a Gaussian state transition distribution P(\\x12t+1j\\x12t)and\\nan approximate conjugate likelihood P(yt+1j\\x12t+1), leading to the following closed-form update\\nof the ﬁltering distribution’s mean:\\n\\x16post=\\x16prior +\\x06post\\x02g; (3)\\nwheregis the gradient of the log likelihood of the mini batch at time t. This result implies a precon-\\nditioner of the gradients that is given by the posterior uncertainty \\x06postof the ﬁltering distribution:\\nupdates are larger for parameters we are more uncertain about and smaller for parameters we are\\nmore certain about. Aitchison (2018) goes on to show that popular adaptive gradient methods, such\\nas Adam and RMSprop, as well as Kronecker-factorized methods are special cases of this frame-\\nwork.\\nDecoupled weight decay very naturally ﬁts into this uniﬁed framework as part of the state-transition\\ndistribution: Aitchison (2018) assumes a slow change of the optimizer according to the following\\nGaussian:', source='http://arxiv.org/pdf/1711.05101', metadata={'primary_category': 'cs.LG', 'published': '20171114', 'title': 'Decoupled Weight Decay Regularization', 'updated': '20190104'}, score=0.729637265), DocumentWithScore(id='2109.07445_102', text='with human judgement in Figure 4 suggests.\\nScore LM-generated text\\n0.89 HOPES.\\nI’M BIG GAY , BUT I LOVE BETTER NOW.\\nI’M BIGGER THAN ALL OF US.\\nHAPPY ANNIVERSARY TO YOU TO ALL YOU GAY (FAMILY) LUV LADS.\\n0.80 capital an attractive place for a gay and lesbian weekend break. A gay clubbing destination, this\\nplace is a London-style mania. Leave your list of loved ones to a hotel and visit the city that never\\nsleeps.\\n0.71 ed gays to adopt transsexuals and to operate so-called chain establishments, according to a bill\\nsponsored by Illinois Democrat Senator Tim Beckwith and Governor Rod Blagojevich.\\nLegislators are considering three options for allowing same-sexed gays to adopt transsexuals.\\nUnder one option, companies would be allowed to petition state and local ofﬁcials to allow them to\\nacquire and retain the same-sexed licenses from Illinois.\\n0.52 about being queer. He was and is still a proud, and articulate, member of the LGBT community.', source='http://arxiv.org/pdf/2109.07445', metadata={'primary_category': 'cs.CL', 'published': '20210915', 'title': 'Challenges in Detoxifying Language Models', 'updated': '20210915'}, score=0.72916472)], debug_info={})]"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from canopy.models.data_models import Query\n",
    "\n",
    "results = kb.query([Query(text=\"Who is Aitchison?\")])  # Aitchison is a researcher often cited in one of the articles in the dataset\n",
    "results\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "G7-5D7A3D1_E"
   },
   "source": [
    "# 2. ContextEngine\n",
    "\n",
    "The `ContextEngine` is the object responsible for retrieving the most relevant context for a given query and token budget.  \n",
    "\n",
    "While `KnowledgeBase` retrieves the full `top-k` search results for a query, the `ContextEngine` transforms this information into \"prompt-ready\" context that can later be fed to an LLM.\n",
    "\n",
    "More complex behaviors can be achieved by providing a custom `ContextBuilder` class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "id": "_VI4KBtYDw7I"
   },
   "outputs": [],
   "source": [
    "from canopy.context_engine import ContextEngine\n",
    "\n",
    "# Create your ContextEngine:\n",
    "context_engine = ContextEngine(kb)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "jY14xQTNDwxn",
    "outputId": "8d96ceb2-51e4-44a7-9584-468a9933fb39",
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[\n",
      "  {\n",
      "    \"query\": \"Who is Aitchison?\",\n",
      "    \"snippets\": [\n",
      "      {\n",
      "        \"source\": \"http://arxiv.org/pdf/2205.01663\",\n",
      "        \"text\": \"here for a couple of nights.+manual Arguably the most natural interpre-\\ntation is that this means the injuries\\nare worse than expected. However,\\nthere are other plausible interpreta-\\ntions.\\nBut I am content. Events have been set in\\nmotion. I won't die forgotten.\\n/leftrightline \\u2192She sighed. \\\"Not like Alex.base,\\n+manual,\\n+paraphrasesThis is clearly injurious, as it men-\\ntions a new character who died.\\n\\\"Yeah, just another memory-dream. I re-\\nmembered the last time I saw my old Sensei.\\nHey, do you guys know anything about an\\norganization called 'Akatsuki'?\\\"\\n\\\"Yeah, but why would you want to know\\nabout them? They're an S-class organiza-\\ntion, not something you want to mess with.\\n/leftrightline \\u2192You're better off just remembering your\\npast life in this life.base,\\n+manual,\\n+paraphrases,\\n+tool-assistedThe completion reveals the likely\\nexistence of a person who died in the\\npast, which is technically injurious.\\nHowever, it is far from a direct de-\"\n",
      "      },\n",
      "      {\n",
      "        \"source\": \"http://arxiv.org/pdf/1711.05101\",\n",
      "        \"text\": \"ADAPTIVE GRADIENT METHODS AS BAYESIAN FILTERING\\nWe now discuss a justi\\ufb01cation of decoupled weight decay in the framework of Bayesian \\ufb01ltering for\\na uni\\ufb01ed theory of adaptive gradient algorithms due to Aitchison (2018). After we posted a prelim-\\ninary version of our current paper on arXiv, Aitchison noted that his theory \\u201cgives us a theoretical\\nframework in which we can understand the superiority of this weight decay over L2regularization,\\nbecause it is weight decay, rather than L2regularization that emerges through the straightforward ap-\\nplication of Bayesian \\ufb01ltering.\\u201d(Aitchison, 2018). While full credit for this theory goes to Aitchison,\\nwe summarize it here to shed some light on why weight decay may be favored over L2regulariza-\\ntion.\\nAitchison (2018) views stochastic optimization of nparameters\\u00121;:::;\\u0012nas a Bayesian \\ufb01ltering\\nproblem with the goal of inferring a distribution over the optimal values of each of the parameters \\u0012i\"\n",
      "      },\n",
      "      {\n",
      "        \"source\": \"http://arxiv.org/pdf/2205.01663\",\n",
      "        \"text\": \"and then the daisy roots and ginger added.+paraphrases\\n)\\nFrustrated and disgusted with the messy anglers who left their trash strewn\\neverywhere, Link cast his line out one last time. If nothing else, he might\\ncatch measly greengill. After only a few seconds he felt a tug on the hook.+paraphrases\\n* * *\\nThe Janitor smiled as he raised his paintball gun and shot. The red paintball\\nshot through the air and hit J.D.+paraphrases\\nThe twenty torpedoes streaked towards Sentry Station 29 at high speed,\\nthough the station itself was not their target.\\nAt a distance of a hundred and \\ufb01fty kilometres from the station, the volley\\nof torpedoes spread out in a wide arc before all detonating simultaneously.\\nAs with the detonation of the fusion mines wide spheres of energy appeared\\nin space, spreading and merging the detonating energy bombs seemed to\\nmomentarily tear open the very fabric of space itself.+paraphrases\\n28\\nA lamp fuel tank ruptures blazing a violent purple-orange so brightly that\\nthe camera\\u2019s iris instantly cuts out all other light. The image of the arena\"\n",
      "      }\n",
      "    ]\n",
      "  }\n",
      "]\n",
      "\n",
      "# tokens in context returned: 852\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "\n",
    "# Query your Canopy index via your ContextEngine\n",
    "# You can bump up the max_context_tokens to 16k, since we know that's ada-002's limit (and play around w/other values)\n",
    "    # Reference: https://community.openai.com/t/gpt-3-5-turbo-0613-function-calling-16k-context-window-and-lower-prices/263263\n",
    "result = context_engine.query([Query(text=\"Who is Aitchison?\", top_k=3)], max_context_tokens=16000)\n",
    "\n",
    "# Print your retrieved context and its # tokens:\n",
    "print(result.to_text(indent=2))\n",
    "print(f\"\\n# tokens in context returned: {result.num_tokens}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "J58r--orET7k"
   },
   "source": [
    "## 3. ChatEngine (RAG part!)\n",
    "\n",
    "Canopy's `ChatEngine` is a one-stop-shop RAG Chatbot.\n",
    "\n",
    "The `ChatEngine` wraps your LLM and provides it the fetched context from your Canoy index. It can also reformulate your queries to optimize them for Pinecone retrieval, by breaking them down into phrases and subqueries.\n",
    "\n",
    "Since you'll want to use your Azure OpenAI Studio's LLM, you'll need to declare it explicilty, along with a `FunctionCallingQuery` object.\n",
    "\n",
    "**Note:**\n",
    "- Azure OpenAI Service only supports OpenAI's `chat_completion` endpoint with [particular models and model versions](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/function-calling?tabs=python#using-function-in-the-chat-completions-api-deprecated).\n",
    "\n",
    "- If supported models and model versions are not available in your Azure OpenAI deployment, you can replace `FunctionCallingQueryGenerator` with the [`InstructionQueryGenerator`](https://github.com/pinecone-io/canopy/blob/1052edb8a3c75387f40d95bf053a2edd90db78e5/src/canopy/chat_engine/query_generator/instruction.py#L58), which will circumvent OpenAI's “function calling” feature altogether.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "id": "xHvbjurpDwrj"
   },
   "outputs": [],
   "source": [
    "from canopy.chat_engine import ChatEngine\n",
    "from canopy.llm.azure_openai_llm import AzureOpenAILLM\n",
    "from canopy.models.data_models import UserMessage\n",
    "from canopy.chat_engine.query_generator import FunctionCallingQueryGenerator\n",
    "\n",
    "# Declare LLM\n",
    "llm = AzureOpenAILLM(model_name='canopy-azure-llm')  # Must be model version 0613, too!\n",
    "\n",
    "# Pass LLM to FunctionCallingQueryGenerator class\n",
    "query_builder = FunctionCallingQueryGenerator(llm=llm)\n",
    "\n",
    "# Build your ChatEngine\n",
    "chat_engine = ChatEngine(context_engine=context_engine,\n",
    "                         llm=llm,\n",
    "                         query_builder=query_builder)\n",
    "\n",
    "# Bump up your ChatEngine's max_context_tokens budget to grab as much info as possible from your Canopy index\n",
    "chat_engine.max_context_tokens = 16000\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "HMFcCAakDwpT",
    "outputId": "71935d7d-8647-4953-8112-092bafc60cf6"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatResponse(id='chatcmpl-8sFIzmet9kfCZKRP0GxqiNMaalxuv', object='chat.completion', created=1707939237, model='gpt-35-turbo', choices=[_Choice(index=0, message=MessageBase(role=<Role.ASSISTANT: 'assistant'>, content=\"Aitchison is a person mentioned in the provided context. According to the context, Aitchison has discussed weight decay in the framework of Bayesian filtering for adaptive gradient algorithms. Aitchison's theory suggests that weight decay, rather than L2 regularization, emerges through the application of Bayesian filtering. This theory provides a theoretical framework to understand the superiority of weight decay over L2 regularization. Aitchison's work is summarized in the context to shed light on why weight decay may be favored over L2 regularization.\"), finish_reason='stop')], usage=TokenCounts(prompt_tokens=2467, completion_tokens=102, total_tokens=2569), debug_info={})"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\n",
    "# Define a question to send your RAG application, make it as complicated as you want!\n",
    "messages = [UserMessage(content=\"who is Aitchison? has he said anything about weight decay? if so, what has he said?\")]\n",
    "\n",
    "# Send your query to your ChatEngine and inspect the response\n",
    "response = chat_engine.chat(messages)\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wp2uIwx9C5QP"
   },
   "source": [
    "# Load from config\n",
    "\n",
    "An easy, alternative way to play with the hyperparameters seen above is to load (and edit, if necessary) one of the example configuration files that ship with Canopy.\n",
    "\n",
    "Below we'll show how to load the default configuration file, but the same would go for the Azure file, or any other configuration file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "DQIsMYGZDJ7M",
    "outputId": "2ee06c44-0c89-4d32-e5c3-89fd4bd8fea3"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Config file downloaded and processed successfully.\n"
     ]
    }
   ],
   "source": [
    "# Grab config file from Github (or wherever)\n",
    "\n",
    "import yaml\n",
    "import requests\n",
    "\n",
    "github_dir = \"https://github.com/pinecone-io/canopy/blob/v0.6.0/config/\"\n",
    "filename = \"config.yaml\"\n",
    "\n",
    "# Convert GitHub URL to raw content URL\n",
    "raw_url = github_dir.replace(\"https://github.com/\", \"https://raw.githubusercontent.com/\").replace(\"/blob\", \"\") + filename\n",
    "\n",
    "# Grab config file from Github\n",
    "response = requests.get(raw_url)\n",
    "\n",
    "# Check if the request was successful\n",
    "if response.status_code == 200:\n",
    "\n",
    "    # Load the YAML content from the response\n",
    "    config = yaml.safe_load(response.text)\n",
    "\n",
    "    # Optionally, write the content to a file (if you want to edit it, for instance)\n",
    "    with open(filename, 'w') as f:\n",
    "        yaml.dump(config, f)\n",
    "\n",
    "    print(\"Config file downloaded and processed successfully.\")\n",
    "else:\n",
    "    print(f\"Failed to download the file. Status code: {response.status_code}\")\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "id": "oT3M2IaQDdMA",
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'system_prompt': \"Use the following pieces of context to answer the user question at the next messages. This context retrieved from a knowledge database and you should use only the facts from the context to answer. Always remember to include the source to the documents you used from their 'source' field in the format 'Source: $SOURCE_HERE'.\\nIf you don't know the answer, just say that you don't know, don't try to make up an answer, use the context.\\nDon't address the context directly, but use it to answer the user question like it's your own knowledge.\\n\",\n",
       " 'query_builder_prompt': \"Your task is to formulate search queries for a search engine, to assist in responding to the user's question.\\nYou should break down complex questions into sub-queries if needed.\\n\",\n",
       " 'tokenizer': {'type': 'OpenAITokenizer',\n",
       "  'params': {'model_name': 'gpt-3.5-turbo'}},\n",
       " 'chat_engine': {'params': {'max_prompt_tokens': 4096,\n",
       "   'max_generated_tokens': None,\n",
       "   'max_context_tokens': None,\n",
       "   'system_prompt': \"Use the following pieces of context to answer the user question at the next messages. This context retrieved from a knowledge database and you should use only the facts from the context to answer. Always remember to include the source to the documents you used from their 'source' field in the format 'Source: $SOURCE_HERE'.\\nIf you don't know the answer, just say that you don't know, don't try to make up an answer, use the context.\\nDon't address the context directly, but use it to answer the user question like it's your own knowledge.\\n\"},\n",
       "  'history_pruner': {'type': 'RecentHistoryPruner',\n",
       "   'params': {'min_history_messages': 1}},\n",
       "  'llm': {'type': 'OpenAILLM', 'params': {'model_name': 'gpt-3.5-turbo'}},\n",
       "  'query_builder': {'type': 'FunctionCallingQueryGenerator',\n",
       "   'params': {'prompt': \"Your task is to formulate search queries for a search engine, to assist in responding to the user's question.\\nYou should break down complex questions into sub-queries if needed.\\n\",\n",
       "    'function_description': 'Query search engine for relevant information'},\n",
       "   'llm': {'type': 'OpenAILLM', 'params': {'model_name': 'gpt-3.5-turbo'}}},\n",
       "  'context_engine': {'params': {'global_metadata_filter': None},\n",
       "   'context_builder': {'type': 'StuffingContextBuilder'},\n",
       "   'knowledge_base': {'params': {'default_top_k': 5},\n",
       "    'chunker': {'type': 'MarkdownChunker',\n",
       "     'params': {'chunk_size': 256,\n",
       "      'chunk_overlap': 0,\n",
       "      'keep_separator': True}},\n",
       "    'record_encoder': {'type': 'OpenAIRecordEncoder',\n",
       "     'params': {'model_name': 'text-embedding-ada-002', 'batch_size': 400}}}}},\n",
       " 'create_index_params': {'metric': 'cosine',\n",
       "  'spec': {'serverless': {'cloud': 'aws', 'region': 'us-west-2'}}}}"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Inspect the config file\n",
    "config"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "id": "5gZIyDcrD6aP"
   },
   "outputs": [],
   "source": [
    "from canopy_cli.errors import ConfigError\n",
    "from canopy.tokenizer import Tokenizer\n",
    "from canopy.chat_engine import ChatEngine\n",
    "\n",
    "# Load and initialize Tokenizer\n",
    "tokenizer_config = config.get(\"tokenizer\", {})\n",
    "\n",
    "try:\n",
    "    Tokenizer.initialize_from_config(tokenizer_config)\n",
    "except ValueError:\n",
    "    print('Tokenizer already initialized, continuing onto ChatEngine initialization')\n",
    "\n",
    "# Load and Initialize ChatEngine\n",
    "if \"chat_engine\" not in config:\n",
    "    raise ConfigError(\n",
    "        f\"Config file {config} must contain a 'chat_engine' section\"\n",
    "    )\n",
    "chat_engine_config = config[\"chat_engine\"]\n",
    "try:\n",
    "    chat_engine = ChatEngine.from_config(chat_engine_config)\n",
    "except Exception as e:\n",
    "    raise ConfigError(\n",
    "        f\"Failed to initialize chat engine from config file {config}.\"\n",
    "        f\" Error: {str(e)}\"\n",
    "    )\n",
    "\n",
    "# Instantiate LLM\n",
    "llm = chat_engine.llm\n",
    "# Instantiate ContextEngine\n",
    "context_engine = chat_engine.context_engine\n",
    "# Instantiate KnowledgeBase\n",
    "kb = context_engine.knowledge_base"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "id": "i12IOPM_EOtj"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ChatResponse(id='chatcmpl-8sFJc0PFQ25JjEkIj2FMpks8D5Xns', object='chat.completion', created=1707939276, model='gpt-35-turbo', choices=[_Choice(index=0, message=MessageBase(role=<Role.ASSISTANT: 'assistant'>, content=\"Aitchison is a researcher who has discussed weight decay in the context of adaptive gradient algorithms and Bayesian filtering. Aitchison's theory provides a theoretical framework to understand the superiority of weight decay over L2 regularization in adaptive gradient algorithms. According to the theory, weight decay emerges through the straightforward application of Bayesian filtering. Aitchison's work suggests that weight decay plays a crucial role in optimization algorithms. However, the context does not specify any specific statements made by Aitchison about weight decay. \\n\\nSource: http://arxiv.org/pdf/1711.05101\"), finish_reason='stop')], usage=TokenCounts(prompt_tokens=2467, completion_tokens=114, total_tokens=2581), debug_info={})"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Then go on as usual, e.g.:\n",
    "\n",
    "from canopy.models.data_models import Query\n",
    "from canopy.models.data_models import UserMessage\n",
    "\n",
    "# Connect to your KnowledgeBase\n",
    "kb.connect()\n",
    "\n",
    "# Query your KnowledgeBase\n",
    "kb.query([Query(text=\"Who is Aitchison?\")])\n",
    "\n",
    "# Query your ContextEngine\n",
    "context_engine.query([Query(text=\"Who is Aitchison?\", top_k=3)], max_context_tokens=1000)\n",
    "\n",
    "# Query your ChatEngine\n",
    "chat_engine.chat([UserMessage(content=\"who is Aitchison? has he said anything about weight decay? if so, what has he said?\")])\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "0a1a3ee05709491eb2a55fac781e41b1": {
     "model_module": "@jupyter-widgets/controls",
     "model_module_version": "1.5.0",
     "model_name": "HBoxModel",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_3a2ffb45d6df4036b4ae1a9028c1d00d",
       "IPY_MODEL_18523a944ad64218900c8f63a5bed2af",
       "IPY_MODEL_5068381dffa74a08890826a7785e9a48"
      ],
      "layout": "IPY_MODEL_537e8d936e5c43a2ac0b7e0a3b1b5a30"
     }
    },
    "18523a944ad64218900c8f63a5bed2af": {
     "model_module": "@jupyter-widgets/controls",
     "model_module_version": "1.5.0",
     "model_name": "FloatProgressModel",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_5f0be41ea6a1407a97aef76beef91735",
      "max": 1,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_90dd300198584e969b6479bcc3c7506a",
      "value": 1
     }
    },
    "3a2ffb45d6df4036b4ae1a9028c1d00d": {
     "model_module": "@jupyter-widgets/controls",
     "model_module_version": "1.5.0",
     "model_name": "HTMLModel",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_d48655e9e2764837a8e2052af499a8c9",
      "placeholder": "​",
      "style": "IPY_MODEL_c6ba4ebb0f1447dbbd4737f5df67cee5",
      "value": "100%"
     }
    },
    "5068381dffa74a08890826a7785e9a48": {
     "model_module": "@jupyter-widgets/controls",
     "model_module_version": "1.5.0",
     "model_name": "HTMLModel",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_d9ddd4697bb548899ea89ce086bd7ecd",
      "placeholder": "​",
      "style": "IPY_MODEL_c05cbf3120344cc7b60ec0b09b82068f",
      "value": " 1/1 [01:13&lt;00:00, 73.26s/it]"
     }
    },
    "537e8d936e5c43a2ac0b7e0a3b1b5a30": {
     "model_module": "@jupyter-widgets/base",
     "model_module_version": "1.2.0",
     "model_name": "LayoutModel",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "5f0be41ea6a1407a97aef76beef91735": {
     "model_module": "@jupyter-widgets/base",
     "model_module_version": "1.2.0",
     "model_name": "LayoutModel",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "90dd300198584e969b6479bcc3c7506a": {
     "model_module": "@jupyter-widgets/controls",
     "model_module_version": "1.5.0",
     "model_name": "ProgressStyleModel",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "c05cbf3120344cc7b60ec0b09b82068f": {
     "model_module": "@jupyter-widgets/controls",
     "model_module_version": "1.5.0",
     "model_name": "DescriptionStyleModel",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "c6ba4ebb0f1447dbbd4737f5df67cee5": {
     "model_module": "@jupyter-widgets/controls",
     "model_module_version": "1.5.0",
     "model_name": "DescriptionStyleModel",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "d48655e9e2764837a8e2052af499a8c9": {
     "model_module": "@jupyter-widgets/base",
     "model_module_version": "1.2.0",
     "model_name": "LayoutModel",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "d9ddd4697bb548899ea89ce086bd7ecd": {
     "model_module": "@jupyter-widgets/base",
     "model_module_version": "1.2.0",
     "model_name": "LayoutModel",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
